home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Games / NetHack 3.1.3 / source / sys / mac / mrecover.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-08-01  |  29.7 KB  |  1,406 lines  |  [TEXT/R*ch]

  1. /*    SCCS Id: @(#)mrecover.c    3.1               93/04/15       */
  2. /*      Copyright (c) David Hairston, 1993.                       */
  3. /* NetHack may be freely redistributed.  See license for details. */
  4.  
  5. /* Macintosh Recovery Application */
  6.  
  7. /* based on code in util/recover.c.  the significant differences are:
  8.  * - define MAC is implicit so config.h is not included (95% laziness).
  9.  * - GUI vs. CLI.  the vast majority of code here supports the GUI.
  10.  * - Mac toolbox equivalents are used in place of ANSI functions.
  11.  * - void restore_savefile(void) is event driven.
  12.  * - integral type substitutions here and there.
  13.  */
  14.  
  15. /*
  16.  * Think C 5.0.4 project specs:
  17.  * signature: 'nhRc'
  18.  * SIZE (-1) info: flags: 0x5880, size: 65536L/65536L (64k/64k)
  19.  * libraries: MacTraps [yes], MacTraps2 (HFileStuff) [yes], ANSI [no]
  20.  * compatibility: system 6 and system 7
  21.  * misc: sizeof(int): 4, "\p": unsigned char, enum size varies,
  22.  *   prototypes required, type checking enforced, no optimizers,
  23.  *   FAR CODE [no], FAR DATA [no], SEPARATE STRS [no], single segment,
  24.  *   short macsbug symbols
  25.  */
  26.  
  27. /*
  28.  * To do (maybe, just maybe):
  29.  * - Merge with the code in util/recover.c.
  30.  * - Document launch (e.g. GUI equivalent of 'recover basename').
  31.  * - Drag and drop.
  32.  * - Internal memory tweaks (stack and heap usage).
  33.  * - Use status file to allow resuming aborted recoveries.
  34.  * - Bundle 'LEVL' files with recover (easier document launch).
  35.  * - Prohibit recovering games "in progress".
  36.  * - Share AppleEvents with NetHack to auto-recover crashed games.
  37.  */
  38.  
  39. #if 1
  40. /************************************************************************\
  41.  * (1) precompile header => mrecover.h, (0) compile code
  42. \************************************************************************/
  43.  
  44. /**** Toolbox defines ****/
  45.  
  46. /* MPW C headers (99.44% pure) */
  47. #include <Types.h>
  48. #include <Errors.h>
  49. #include <Memory.h>
  50. #include <OSUtils.h>
  51. #include <Resources.h>
  52. #include <Files.h>
  53. #include <SysEqu.h>
  54. #include <SegLoad.h>
  55.  
  56. #include <Quickdraw.h>
  57. #include <Fonts.h>
  58. #include <Windows.h>
  59. #include <Menus.h>
  60. #include <Dialogs.h>
  61.  
  62. #include <Desk.h>
  63. #include <DiskInit.h>
  64. #include <Events.h>
  65. #include <Notification.h>
  66. #include <Packages.h>
  67. #include <Script.h>
  68. #include <StandardFile.h>
  69. #include <ToolUtils.h>
  70.  
  71. #if 1    /* glue for System 7 Icon Family call (needed by Think C 5.0.4) */
  72. pascal OSErr GetIconSuite(Handle *theIconSuite, short theResID, long selector)
  73.     = {0x303C, 0x0501, 0xABC9};
  74. #endif
  75.  
  76.  
  77. /**** Application defines ****/
  78.  
  79. /* Memory */
  80. typedef struct memBytes    /* format of 'memB' resource, preloaded/locked */
  81. {
  82.     short    memReserved;
  83.     short    memCleanup;    /* 4   - memory monitor activity limit */
  84.     long    memPreempt;    /* 32k - start iff FreeMem() > */
  85.     long    memWarning;    /* 12k - warn if MaxMem() < */
  86.     long    memAbort;    /* 4k  - abort if MaxMem() < */
  87.     long    memIOBuf;    /* 16k - read/write buffer size */
  88. } memBytes, *memBytesPtr, **memBytesHandle;
  89.  
  90. #define membID            128                /* 'memB' resource ID */
  91.  
  92.  
  93. /* Cursor */
  94. #define CURS_FRAME        4L                /* 1/15 second - spin cursor */
  95. #define CURS_LATENT        60L                /* pause before spin cursor */
  96. #define curs_Init        (-1)            /* token for set arrow */
  97. #define curs_Total        8                /* maybe 'acur' would be better */
  98. #define cursorOffset    128                /* GetCursor(cursorOffset + i) */
  99.  
  100.  
  101. /* Menu */
  102. enum
  103. {
  104.     mbar_Init = -1,
  105.     mbarAppl,                            /* normal mode */
  106.     mbarRecover,                        /* in recovery mode */
  107.     mbarDA                                /* DA in front mode */
  108. };
  109. enum
  110. {
  111.     menuApple,
  112.     menuFile,
  113.     menuEdit,
  114.     menu_Total,
  115.  
  116.     muidApple = 128,
  117.     muidFile,
  118.     muidEdit
  119. };
  120. enum
  121. {
  122.     /* Apple menu */
  123.     mitmAbout = 1,
  124.     mitmHelp,
  125.     ____128_1,
  126.  
  127.     /* File menu */
  128.     mitmOpen = 1,
  129.     ____129_1,
  130.     mitmClose_DA,
  131.     ____129_2,
  132.     mitmQuit
  133.  
  134.     /* standard minimum required Edit menu */
  135. };
  136.  
  137.  
  138. /* Alerts */
  139. enum
  140. {
  141.     alrtNote,                            /* general messages */
  142.     alrtHelp,                            /* help message */
  143.     alrt_Total,
  144.  
  145.     alertAppleMenu = 127,                /* menuItem to alert ID offset */
  146.     alidNote,
  147.     alidHelp
  148. };
  149.  
  150. #define aboutBufSize    80                /* i.e. 2 lines of 320 pixels */
  151.  
  152.  
  153. /* Notification */
  154. #define nmBufSize        (32 + aboutBufSize + 32)
  155. typedef struct notifRec
  156. {
  157.     NMRec                nmr;
  158.     struct notifRec    *    nmNext;
  159.     short                nmDispose;
  160.     unsigned char        nmBuf[nmBufSize];
  161. } notifRec, *notifPtr;
  162.  
  163. #define nmPending        nmRefCon            /* &in.Notify */
  164. #define iconNotifyID    128
  165. #define ics_1_and_4        0x00000300
  166.  
  167. /* Dialogs */
  168. enum
  169. {
  170.     dlogProgress = 256
  171. };
  172. enum
  173. {
  174.     uitmThermo = 1
  175. };
  176. enum
  177. {
  178.     initItem,
  179.     invalItem,
  180.     drawItem
  181. };
  182.  
  183.  
  184. /* Miscellaneous */
  185. typedef struct modeFlags
  186. {
  187.     short    Front;            /* fg/bg event handling */
  188.     short    Notify;            /* level of pending NM notifications */
  189.     short    Dialog;            /* a modeless dialog is open */
  190.     short    Recover;        /* restoration progress index */
  191. } modeFlags;
  192.  
  193. /* convenient define to allow easier (for me) parsing of 'vers' resource */
  194. typedef struct versXRec
  195. {
  196.     NumVersion        numVers;
  197.     short            placeCode;
  198.     unsigned char    versStr[];    /* (small string)(large string) */
  199. } versXRec, *versXPtr, **versXHandle;
  200.  
  201. #else
  202. /************************************************************************\
  203.  * compile source code
  204. \************************************************************************/
  205.  
  206. #include "mrecover.h"
  207.  
  208. /**** Global variables ****/
  209. modeFlags        in = {1};                /* in Front */
  210. EventRecord        wnEvt;
  211. SysEnvRec        sysEnv;
  212. unsigned char    aboutBuf[aboutBufSize];    /* vers 1 "Get Info" string */
  213. memBytesPtr        pBytes;                    /* memory management */
  214. unsigned short    memActivity;            /* more memory management */
  215. MenuHandle        mHnd[menu_Total];
  216. CursPtr            cPtr[curs_Total];        /* busy cursors */
  217. unsigned long    timeCursor;                /* next cursor frame time */
  218. short            oldCursor = curs_Init;    /* see adjustGUI() below */
  219. notifPtr        pNMQ;                    /* notification queue pointer */
  220. notifRec        nmt;                    /* notification template */
  221. DialogTHndl        thermoTHnd;
  222. DialogRecord    dlgThermo;                /* progress thermometer */
  223. #define DLGTHM    ((DialogPtr) &dlgThermo)
  224. #define WNDTHM    ((WindowPtr) &dlgThermo)
  225. #define GRFTHM    ((GrafPtr) &dlgThermo)
  226.  
  227. Point            sfGetWhere;                /* top left corner of get file dialog */
  228. Ptr                pIOBuf;                    /* read/write buffer pointer */
  229. short            vRefNum;                /* SFGetFile working directory/volume refnum */
  230. long            dirID;                    /* directory i.d. */
  231.  
  232. #define CREATOR        'nh31'                /* NetHack signature */
  233. #define SAVETYPE    'SAVE'                /* save file type */
  234. #define FILENAME    256                    /* macconf.h */
  235. typedef signed char    schar;                /* config.h */
  236. typedef schar        xchar;                /* global.h */
  237. #define MAX_RECOVER_COUNT    256
  238.  
  239. #define APP_NAME_RES_ID        (-16396)    /* macfile.h */
  240. #define PLAYER_NAME_RES_ID    1001        /* macfile.h */
  241.  
  242. /* variables from util/recover.c */
  243. #define SAVESIZE    FILENAME
  244. unsigned char    savename[SAVESIZE];        /* originally a C string */
  245. unsigned char    lock[256];                /* pascal string */
  246.  
  247. long            hpid;                    /* NetHack (unix-style) process i.d. */
  248. short            saveRefNum;                /* save file descriptor */
  249. short            gameRefNum;                /* level 0 file descriptor */
  250. short            levRefNum;                /* level n file descriptor */
  251.  
  252.  
  253. /**** Prototypes ****/
  254. static    void warmup(void);
  255. static    Handle alignTemplate(ResType, short, short, short, Point *);
  256. pascal    void nmCompletion(NMRec *);
  257. static    void noteErrorMessage(unsigned char *, unsigned char *);
  258. static    void note(short, short, unsigned char *);
  259. static    void adjustGUI(void);
  260. static    void adjustMemory(void);
  261. static    void optionMemStats(void);
  262. static    void MenuEvent(long);
  263. static    void eventLoop(void);
  264. static    void cooldown(void);
  265.  
  266. pascal    void drawThermo(WindowPtr, short);
  267. static    void itemizeThermo(short);
  268. pascal    Boolean basenameFileFilter(ParmBlkPtr);
  269. static    void beginRecover(void);
  270. static    void continueRecover(void);
  271. static    void endRecover(void);
  272. static    short saveRezStrings(void);
  273.  
  274. /* analogous prototypes from util/recover.c */
  275. static    void set_levelfile_name(long);
  276. static    short open_levelfile(long);
  277. static    short create_savefile(unsigned char *);
  278. static    void copy_bytes(short, short);
  279. static    void restore_savefile(void);
  280.  
  281. /* auxiliary prototypes */
  282. static    long read_levelfile(short, Ptr, long);
  283. static    long write_savefile(short, Ptr, long);
  284. static    void close_file(short *);
  285. static    void unlink_file(unsigned char *);
  286.  
  287.  
  288. /**** Routines ****/
  289.  
  290. main()
  291. {
  292.     /* heap adjust */
  293.     MaxApplZone();
  294.     MoreMasters();
  295.     MoreMasters();
  296.  
  297.     /* manager initialization */
  298.     InitGraf(&qd.thePort);
  299.     InitFonts();
  300.     InitWindows();
  301.     InitMenus();
  302.     TEInit();
  303.     InitDialogs((ResumeProcPtr) 0);
  304.     InitCursor();
  305.  
  306.     /* get system environment, notification requires 6.0 or better */
  307.     (void) SysEnvirons(curSysEnvVers, &sysEnv);
  308.     if (sysEnv.systemVersion < 0x0600)
  309.     {
  310.         ParamText("\pAbort: System 6.0 is required", "\p", "\p", "\p");
  311.         (void) Alert(alidNote, (ModalFilterProcPtr) 0L);
  312.         ExitToShell();
  313.     }
  314.  
  315.     warmup();
  316.     eventLoop();
  317.  
  318.     /* normally these routines are never reached from here */
  319.     cooldown();
  320.     ExitToShell();
  321. }
  322.  
  323. static void
  324. warmup()
  325. {
  326.     short        i;
  327.  
  328.     /* pre-System 7 MultiFinder hack for smooth launch */
  329.     for (i = 0; i < 10; i++)
  330.     {
  331.         if (WaitNextEvent(osMask, &wnEvt, 2L, (RgnHandle) 0L))
  332.             if (((wnEvt.message & osEvtMessageMask) >> 24) == suspendResumeMessage)
  333.                 in.Front = (wnEvt.message & resumeFlag);
  334.     }
  335.  
  336.     /* clear out the Finder info */
  337.     {
  338.         short    message, count;
  339.  
  340.         CountAppFiles(&message, &count);
  341.         while(count)
  342.             ClrAppFiles(count--);
  343.     }
  344.  
  345.     /* fill out the notification template */
  346.     nmt.nmr.qType = nmType;
  347.     nmt.nmr.nmMark = 1;
  348.     nmt.nmr.nmSound = (Handle) -1L;        /* system beep */
  349.     nmt.nmr.nmStr = nmt.nmBuf;
  350.     nmt.nmr.nmResp = nmCompletion;
  351.     nmt.nmr.nmPending = (long) &in.Notify;
  352.  
  353.     /* prepend app name (31 chars or less) to notification buffer */
  354.     {
  355.         short    apRefNum;
  356.         Handle    apParams;
  357.  
  358.         GetAppParms(* (Str255 *) &nmt.nmBuf, &apRefNum, &apParams);
  359.     }
  360.  
  361.     /* add formatting (two line returns) */
  362.     nmt.nmBuf[++(nmt.nmBuf[0])] = '\r';
  363.     nmt.nmBuf[++(nmt.nmBuf[0])] = '\r';
  364.  
  365.     /**** note() is usable now but not aesthetically complete ****/
  366.  
  367.     /* get notification icon */
  368.     if (sysEnv.systemVersion < 0x0700)
  369.     {
  370.         if (! (nmt.nmr.nmIcon = GetResource('SICN', iconNotifyID)))
  371.             note(nilHandleErr, 0, "\pNil SICN Handle");
  372.     }
  373.     else
  374.     {
  375.         if (GetIconSuite(&nmt.nmr.nmIcon, iconNotifyID, ics_1_and_4))
  376.             note(nilHandleErr, 0, "\pBad Icon Family");
  377.     }
  378.  
  379.     /* load and align various dialog/alert templates */
  380.     (void) alignTemplate('ALRT', alidNote, 0, 4, (Point *) 0L);
  381.     (void) alignTemplate('ALRT', alidHelp, 0, 4, (Point *) 0L);
  382.  
  383.     thermoTHnd = (DialogTHndl) alignTemplate('DLOG', dlogProgress, 20, 8, (Point *) 0L);
  384.  
  385.     (void) alignTemplate('DLOG', getDlgID, 0, 6, (Point *) &sfGetWhere);
  386.  
  387.     /* get the "busy cursors" (preloaded/locked) */
  388.     for (i = 0; i < curs_Total; i++)
  389.     {
  390.         CursHandle        cHnd;
  391.  
  392.         if (! (cHnd = GetCursor(i + cursorOffset)))
  393.             note(nilHandleErr, 0, "\pNil CURS Handle");
  394.  
  395.         cPtr[i] = *cHnd;
  396.     }
  397.  
  398.     /* get the 'vers' 1 long (Get Info) string - About Recover... */
  399.     {
  400.         versXHandle        vHnd;
  401.  
  402.         if (! (vHnd = (versXHandle) GetResource('vers', 1)))
  403.             note(nilHandleErr, 0, "\pNil vers Handle");
  404.  
  405.         i = (**vHnd).versStr[0] + 1;        /* offset to Get Info pascal string */
  406.  
  407.         if ((aboutBuf[0] = (**vHnd).versStr[i]) > (aboutBufSize - 1))
  408.             aboutBuf[0] = aboutBufSize - 1;
  409.  
  410.         i++;
  411.  
  412.         MoveHHi((Handle) vHnd);            /* DEE - Fense ... */
  413.         HLock((Handle) vHnd);
  414.         BlockMove(&((**vHnd).versStr[i]), &(aboutBuf[1]), aboutBuf[0]);
  415.         ReleaseResource((Handle) vHnd);
  416.     }
  417.  
  418.     /* form the menubar */
  419.     for (i = 0; i < menu_Total; i++)
  420.     {
  421.         if (! (mHnd[i] = GetMenu(i + muidApple)))
  422.             note(nilHandleErr, 0, "\pNil MENU Handle");
  423.  
  424.         /* expand the apple menu */
  425.         if (i == menuApple)
  426.             AddResMenu(mHnd[menuApple], 'DRVR');
  427.  
  428.         InsertMenu(mHnd[i], 0);
  429.     }
  430.  
  431.     /* pre-emptive memory check */
  432.     {
  433.         memBytesHandle    hBytes;
  434.         Size            grow;
  435.  
  436.         if (! (hBytes = (memBytesHandle) GetResource('memB', membID)))
  437.             note(nilHandleErr, 0, "\pNil Memory Handle");
  438.  
  439.         pBytes = *hBytes;
  440.  
  441.         if (MaxMem(&grow) < pBytes->memPreempt)
  442.             note(memFullErr, 0, "\pMore Memory Required\rTry adding 16k");
  443.  
  444.         memActivity = pBytes->memCleanup;        /* force initial cleanup */
  445.     }
  446.  
  447.     /* get the I/O buffer */
  448.     if (! (pIOBuf = NewPtr(pBytes->memIOBuf)))
  449.         note(memFullErr, 0, "\pNil I/O Pointer");
  450. }
  451.  
  452. /* align a window-related template to the main screen */
  453. static Handle
  454. alignTemplate(ResType rezType, short rezID, short vOff, short vDenom, Point *pPt)
  455. {
  456.     Handle    rtnHnd;
  457.     Rect    *pRct;
  458.  
  459.     vOff += GetMBarHeight();
  460.  
  461.     if (! (rtnHnd = GetResource(rezType, rezID)))
  462.         note(nilHandleErr, 0, "\pNil Template Handle");
  463.  
  464.     pRct = (Rect *) *rtnHnd;
  465.  
  466.     /* don't move memory while aligning rect */
  467.     pRct->right -= pRct->left;        /* width */
  468.     pRct->bottom -= pRct->top;        /* height */
  469.     pRct->left = (qd.screenBits.bounds.right - pRct->right) / 2;
  470.     pRct->top = (qd.screenBits.bounds.bottom - pRct->bottom - vOff) / vDenom;
  471.     pRct->top += vOff;
  472.     pRct->right += pRct->left;
  473.     pRct->bottom += pRct->top;
  474.  
  475.     if (pPt)
  476.         *pPt = * (Point *) pRct;    /* top left corner */
  477.  
  478.     return rtnHnd;
  479. }
  480.  
  481. /* notification completion routine */
  482. pascal void
  483. nmCompletion(NMRec * pNMR)
  484. {
  485.     (void) NMRemove(pNMR);
  486.  
  487.     (* (short *) (pNMR->nmPending))--;    /* decrement pending note level */
  488.     ((notifPtr) pNMR)->nmDispose = 1;    /* allow DisposPtr() */
  489. }
  490.  
  491. /*
  492.  * handle errors inside of note().  the error message is appended to the
  493.  * given message but on a separate line and must fit within nmBufSize.
  494.  */
  495. static void
  496. noteErrorMessage(unsigned char *msg, unsigned char *errMsg)
  497. {
  498.     short    i = nmt.nmBuf[0] + 1;        /* insertion point */
  499.  
  500.     BlockMove(&msg[1], &nmt.nmBuf[i], msg[0]);
  501.     nmt.nmBuf[i + msg[0]] = '\r';
  502.     nmt.nmBuf[0] += (msg[0] + 1);
  503.  
  504.     note(memFullErr, 0, errMsg);
  505. }
  506.  
  507. /*
  508.  * display messages using Notification Manager or an alert.
  509.  * no run-length checking is done.  the messages are created to fit
  510.  * in the allocated space (nmBufSize and aboutBufSize).
  511.  */
  512. static void
  513. note(short errorSignal, short alertID, unsigned char *msg)
  514. {
  515.     if (! errorSignal)
  516.     {
  517.         Size    grow;
  518.  
  519.         if (MaxMem(&grow) < pBytes->memAbort)
  520.             noteErrorMessage(msg, "\pOut of Memory");
  521.     }
  522.  
  523.     if (errorSignal || !in.Front)
  524.     {
  525.         notifPtr    pNMR;
  526.         short        i = nmt.nmBuf[0] + 1;    /* insertion point */
  527.  
  528.         if (errorSignal)        /* use notification template */
  529.         {
  530.             pNMR = &nmt;
  531.  
  532.             /* we're going to abort so add in this prefix */
  533.             BlockMove("Abort: ", &nmt.nmBuf[i], 7);
  534.             i += 7;
  535.             nmt.nmBuf[0] += 7;
  536.         }
  537.         else                    /* allocate a notification record */
  538.         {
  539.             if (! (pNMR = (notifPtr) NewPtr(sizeof(notifRec))))
  540.                 noteErrorMessage(msg, "\pNil New Pointer");
  541.  
  542.             /* initialize it */
  543.             *pNMR = nmt;
  544.             pNMR->nmr.nmStr = (StringPtr) &(pNMR->nmBuf);
  545.  
  546.             /* update the notification queue */
  547.             if (!pNMQ)
  548.                 pNMQ = pNMR;
  549.             else
  550.             {
  551.                 notifPtr    pNMX;
  552.  
  553.                 /* find the end of the queue */
  554.                 for (pNMX = pNMQ; pNMX->nmNext; pNMX = pNMX->nmNext)
  555.                     ;
  556.  
  557.                 pNMX->nmNext = pNMR;
  558.             }
  559.         }
  560.  
  561.         /* concatenate the message */
  562.         BlockMove(&msg[1], &((pNMR->nmBuf)[i]), msg[0]);
  563.         (pNMR->nmBuf)[0] += msg[0];
  564.  
  565.         in.Notify++;            /* increase note pending level */
  566.  
  567.         NMInstall((NMRec *) pNMR);
  568.  
  569.         if (errorSignal)
  570.             cooldown();
  571.  
  572.         return;
  573.     }
  574.  
  575.     /* in front and no error so use an alert */
  576.     ParamText(msg, "\p", "\p", "\p");
  577.     (void) Alert(alertID, (ModalFilterProcPtr) 0L);
  578.     ResetAlrtStage();
  579.  
  580.     memActivity++;
  581. }
  582.  
  583. static void
  584. adjustGUI()
  585. {
  586.     static short    oldMenubar = mbar_Init;    /* force initial update */
  587.     short            newMenubar;
  588.     WindowPeek        frontWindow;
  589.  
  590.     /* oldCursor is external so it can be reset in endRecover() */
  591.     static short    newCursor = curs_Init;
  592.     unsigned long    timeNow;
  593.     short            useArrow;
  594.  
  595.     /* adjust menubar 1st */
  596.     newMenubar = in.Recover ? mbarRecover : mbarAppl;
  597.  
  598.     /* desk accessories take precedence */
  599.     if (frontWindow = (WindowPeek) FrontWindow())
  600.         if (frontWindow->windowKind < 0)
  601.             newMenubar = mbarDA;
  602.  
  603.     if (newMenubar != oldMenubar)
  604.     {
  605.         /* adjust menus */
  606.         switch (oldMenubar = newMenubar)
  607.         {
  608.         case mbarAppl:
  609.             EnableItem(mHnd[menuFile], mitmOpen);
  610.             SetItemMark(mHnd[menuFile], mitmOpen, noMark);
  611.             DisableItem(mHnd[menuFile], mitmClose_DA);
  612.             DisableItem(mHnd[menuEdit], 0);
  613.             break;
  614.  
  615.         case mbarRecover:
  616.             DisableItem(mHnd[menuFile], mitmOpen);
  617.             SetItemMark(mHnd[menuFile], mitmOpen, checkMark);
  618.             DisableItem(mHnd[menuFile], mitmClose_DA);
  619.             DisableItem(mHnd[menuEdit], 0);
  620.             break;
  621.  
  622.         case mbarDA:
  623.             DisableItem(mHnd[menuFile], mitmOpen);
  624.             EnableItem(mHnd[menuFile], mitmClose_DA);
  625.             EnableItem(mHnd[menuEdit], 0);
  626.             break;
  627.         }
  628.  
  629.         DrawMenuBar();
  630.     }
  631.  
  632.     /* now adjust the cursor */
  633.     if (useArrow = (!in.Recover || (newMenubar == mbarDA)))
  634.         newCursor = curs_Init;
  635.     else if ((timeNow = TickCount()) >= timeCursor)        /* spin cursor */
  636.     {
  637.         timeCursor = timeNow + CURS_FRAME;
  638.         if (++newCursor >= curs_Total)
  639.             newCursor = 0;
  640.     }
  641.  
  642.     if (newCursor != oldCursor)
  643.     {
  644.         oldCursor = newCursor;
  645.  
  646.         SetCursor(useArrow ? &qd.arrow : cPtr[newCursor]);
  647.     }
  648. }
  649.  
  650. static void
  651. adjustMemory()
  652. {
  653.     Size        grow;
  654.  
  655.     memActivity = 0;
  656.  
  657.     if (MaxMem(&grow) < pBytes->memWarning)
  658.         note(noErr, alidNote, "\pWarning: Memory is running low");
  659.  
  660.     (void) ResrvMem((Size) FreeMem());        /* move all handles high */
  661. }
  662.  
  663. /* show memory stats: FreeMem, MaxBlock, PurgeSpace, and StackSpace */
  664. static void
  665. optionMemStats()
  666. {
  667.     unsigned char    *pFormat = "\pFree:#k  Max:#k  Purge:#k  Stack:#k";
  668.     char            *pSub = "#";        /* not a pascal string */
  669.     unsigned char    nBuf[16];
  670.     long            nStat, contig;
  671.     Handle            strHnd;
  672.     long            nOffset;
  673.     short            i;
  674.  
  675.     if (wnEvt.modifiers & shiftKey)
  676.         adjustMemory();
  677.  
  678.     if (! (strHnd = NewHandle((Size) 128)))
  679.     {
  680.         note(noErr, alidNote, "\pOops: Memory stats unavailable!");
  681.         return;
  682.     }
  683.     
  684.     SetString((StringHandle) strHnd, pFormat);
  685.     nOffset = 1L;
  686.  
  687.     for (i = 1; i <= 4; i++)
  688.     {
  689.         /* get the replacement number stat */
  690.         switch (i)
  691.         {
  692.         case 1: nStat = FreeMem();                break;
  693.         case 2: nStat = MaxBlock();                break;
  694.         case 3: PurgeSpace(&nStat, &contig);    break;
  695.         case 4: nStat = StackSpace();            break;
  696.         }
  697.  
  698.         NumToString((nStat >> 10), * (Str255 *) &nBuf);
  699.  
  700.         **strHnd += nBuf[0] - 1;
  701.         nOffset = Munger(strHnd, nOffset, (Ptr) pSub, 1L, (Ptr) &nBuf[1], nBuf[0]);
  702.     }
  703.  
  704.     MoveHHi(strHnd);
  705.     HLock(strHnd);
  706.     note(noErr, alidNote, (unsigned char *) *strHnd);
  707.     DisposHandle(strHnd);
  708. }
  709.  
  710. static void
  711. MenuEvent(long menuEntry)
  712. {
  713.     short menuID = HiWord(menuEntry);
  714.     short menuItem = LoWord(menuEntry);
  715.  
  716.     switch (menuID)
  717.     {
  718.     case muidApple:
  719.         switch (menuItem)
  720.         {
  721.         case mitmAbout:
  722.             if (wnEvt.modifiers & optionKey)
  723.                 optionMemStats();
  724.             /* fall thru */
  725.         case mitmHelp:
  726.             note(noErr, (alertAppleMenu + menuItem), aboutBuf);
  727.             break;
  728.  
  729.         default:    /* DA's or apple menu items */
  730.             {
  731.                 unsigned char    daName[32];
  732.  
  733.                 GetItem(mHnd[menuApple], menuItem, * (Str255 *) &daName);
  734.                 (void) OpenDeskAcc(daName);
  735.  
  736.                 memActivity++;
  737.             }
  738.             break;
  739.         }
  740.         break;
  741.  
  742.     case muidFile:
  743.         switch (menuItem)
  744.         {
  745.         case mitmOpen:
  746.             beginRecover();
  747.             break;
  748.  
  749.         case mitmClose_DA:
  750.             {
  751.                 WindowPeek    frontWindow;
  752.                 short        refNum;
  753.  
  754.                 if (frontWindow = (WindowPeek) FrontWindow())
  755.                     if ((refNum = frontWindow->windowKind) < 0)
  756.                         CloseDeskAcc(refNum);
  757.  
  758.                 memActivity++;
  759.             }
  760.             break;
  761.  
  762.         case mitmQuit:
  763.             cooldown();
  764.             break;
  765.         }
  766.         break;
  767.  
  768.     case muidEdit:
  769.         (void) SystemEdit(menuItem - 1);
  770.         break;
  771.     }
  772.  
  773.     HiliteMenu(0);
  774. }
  775.  
  776. static void
  777. eventLoop()
  778. {
  779.     short    wneMask = (in.Front ? everyEvent : (osMask + updateMask));
  780.     long    wneSleep = (in.Front ? 0L : 3L);
  781.  
  782.     while (1)
  783.     {
  784.         if (in.Front)
  785.             adjustGUI();
  786.  
  787.         if (memActivity >= pBytes->memCleanup)
  788.             adjustMemory();
  789.  
  790.         (void) WaitNextEvent(wneMask, &wnEvt, wneSleep, (RgnHandle) 0L);
  791.  
  792.         if (in.Dialog)
  793.             (void) IsDialogEvent(&wnEvt);
  794.  
  795.         switch (wnEvt.what)
  796.         {
  797.         case osEvt:
  798.             if (((wnEvt.message & osEvtMessageMask) >> 24) == suspendResumeMessage)
  799.             {
  800.                 in.Front = (wnEvt.message & resumeFlag);
  801.                 wneMask = (in.Front ? everyEvent : (osMask + updateMask));
  802.                 wneSleep = (in.Front ? 0L : 3L);
  803.             }
  804.             break;
  805.  
  806.         case nullEvent:
  807.             /* adjust the FIFO notification queue */
  808.             if (pNMQ && pNMQ->nmDispose)
  809.             {
  810.                 notifPtr pNMX = pNMQ->nmNext;
  811.  
  812.                 DisposPtr((Ptr) pNMQ);
  813.                 pNMQ = pNMX;
  814.  
  815.                 memActivity++;
  816.             }
  817.  
  818.             if (in.Recover)
  819.                 continueRecover();
  820.             break;
  821.  
  822.         case mouseDown:
  823.             {
  824.                 WindowPtr    whichWindow;
  825.                 
  826.                 switch(FindWindow( wnEvt . where , &whichWindow))
  827.                 {
  828.                 case inMenuBar:
  829.                     MenuEvent(MenuSelect( wnEvt . where ));
  830.                     break;
  831.  
  832.                 case inSysWindow:
  833.                     SystemClick(&wnEvt, whichWindow);
  834.                     break;
  835.  
  836.                 case inDrag:
  837.                     {
  838.                         Rect    boundsRect = qd.screenBits.bounds;
  839.                         Point    offsetPt;
  840.  
  841.                         InsetRect(&boundsRect, 4, 4);
  842.                         boundsRect.top += GetMBarHeight();
  843.  
  844.                         DragWindow(whichWindow, * ((Point *) &wnEvt.where), &boundsRect);
  845.  
  846.                         boundsRect = whichWindow->portRect;
  847.                         offsetPt = * (Point *) &(whichWindow->portBits.bounds);
  848.                         OffsetRect(&boundsRect, -offsetPt.h, -offsetPt.v);
  849.  
  850.                         * (Rect *) *thermoTHnd = boundsRect;
  851.                     }
  852.                     break;
  853.                 }
  854.             }
  855.             break;
  856.  
  857.         case keyDown:
  858.             {
  859.                 char    key = (wnEvt.message & charCodeMask);
  860.  
  861.                 if (wnEvt.modifiers & cmdKey)
  862.                 {
  863.                     if (key == '.')
  864.                     {
  865.                         if (in.Recover)
  866.                         {
  867.                             endRecover();
  868.                             note(noErr, alidNote, "\pSorry: Recovery aborted");
  869.                         }
  870.                     }
  871.                     else
  872.                         MenuEvent(MenuKey(key));
  873.                 }
  874.             }
  875.             break;
  876.  
  877.         /* without windows these events belong to our thermometer */
  878.         case updateEvt:
  879.         case activateEvt:
  880.         {
  881.             DialogPtr    dPtr;
  882.             short        itemHit;
  883.  
  884.             (void) DialogSelect(&wnEvt, &dPtr, &itemHit);
  885.         }
  886.  
  887.         case diskEvt:
  888.             if (HiWord(wnEvt.message))
  889.             {
  890.                 Point    pt = {60, 60};
  891.  
  892.                 (void) DIBadMount(pt, wnEvt.message);
  893.                 DIUnload();
  894.  
  895.                 memActivity++;
  896.             }
  897.             break;
  898.         }            /* switch (wnEvt.what) */
  899.     }                /* while (1) */
  900. }
  901.  
  902. static void
  903. cooldown()
  904. {
  905.     if (in.Recover)
  906.         endRecover();
  907.  
  908.     /* wait for pending notifications to complete */
  909.     while (in.Notify)
  910.         (void) WaitNextEvent(0, &wnEvt, 3L, (RgnHandle) 0L);
  911.  
  912.     ExitToShell();
  913. }
  914.  
  915. /* draw the progress thermometer and frame.  1 level <=> 1 horiz. pixel */
  916. pascal void
  917. drawThermo(WindowPtr wPtr, short inum)
  918. {
  919.     itemizeThermo(drawItem);
  920. }
  921.  
  922. /* manage progress thermometer dialog */
  923. static void
  924. itemizeThermo(short itemMode)
  925. {
  926.     short    iTyp, iTmp;
  927.     Handle    iHnd;
  928.     Rect    iRct;
  929.  
  930.     GetDItem(DLGTHM, uitmThermo, &iTyp, &iHnd, &iRct);
  931.  
  932.     switch(itemMode)
  933.     {
  934.     case initItem:
  935.         SetDItem(DLGTHM, uitmThermo, iTyp, (Handle) drawThermo, &iRct);
  936.         break;
  937.  
  938.     case invalItem:
  939.         {
  940.             GrafPtr    oldPort;
  941.  
  942.             GetPort(&oldPort);
  943.             SetPort(GRFTHM);
  944.  
  945.             InsetRect(&iRct, 1, 1);
  946.             InvalRect(&iRct);
  947.  
  948.             SetPort(oldPort);
  949.         }
  950.         break;
  951.  
  952.     case drawItem:
  953.             FrameRect(&iRct);
  954.             InsetRect(&iRct, 1, 1);
  955.  
  956.             iTmp = iRct.right;
  957.             iRct.right = iRct.left + in.Recover;
  958.             PaintRect(&iRct);
  959.  
  960.             iRct.left = iRct.right;
  961.             iRct.right = iTmp;
  962.             EraseRect(&iRct);
  963.         break;
  964.     }
  965. }
  966.  
  967. /* show only <pid-plname>.0 files in get file dialog */
  968. pascal Boolean
  969. basenameFileFilter(ParmBlkPtr pPB)
  970. {
  971.     unsigned char    *pC;
  972.  
  973.     if (! (pC = (unsigned char *) pPB->fileParam.ioNamePtr))
  974.         return true;
  975.  
  976.     if ((*pC < 4) || (*pC > 28))                        /* save/ 1name .0 */
  977.         return true;
  978.  
  979.     if ((pC[*pC - 1] == '.') && (pC[*pC] == '0'))        /* bingo! */
  980.         return false;
  981.  
  982.     return true;
  983. }
  984.  
  985. static void
  986. beginRecover()
  987. {
  988.     SFTypeList        levlType = {'LEVL'};
  989.     SFReply            sfGetReply;
  990.  
  991.     SFGetFile(sfGetWhere, "\p", &basenameFileFilter, 1, levlType,
  992.                 (DlgHookProcPtr) 0L, &sfGetReply);
  993.  
  994.     memActivity++;
  995.  
  996.     if (! sfGetReply.good)
  997.         return;
  998.  
  999.     /* get volume (working directory) refnum, basename, and directory i.d. */
  1000.     vRefNum = sfGetReply.vRefNum;
  1001.     BlockMove(sfGetReply.fName, lock, sfGetReply.fName[0] + 1);
  1002.     {
  1003.         static CInfoPBRec    catInfo;
  1004.  
  1005.         catInfo.hFileInfo.ioNamePtr = (StringPtr) sfGetReply.fName;
  1006.         catInfo.hFileInfo.ioVRefNum = sfGetReply.vRefNum;
  1007.         catInfo.hFileInfo.ioDirID = 0L;
  1008.  
  1009.         if (PBGetCatInfoSync(&catInfo))
  1010.         {
  1011.             note(noErr, alidNote, "\pSorry: Bad File Info");
  1012.             return;
  1013.         }
  1014.  
  1015.         dirID = catInfo.hFileInfo.ioFlParID;
  1016.     }
  1017.  
  1018.     /* open the progress thermometer dialog */
  1019.     (void) GetNewDialog(dlogProgress, (Ptr) &dlgThermo, (WindowPtr) -1L);
  1020.     if (ResError() || MemError())
  1021.         note(noErr, alidNote, "\pOops: Progress thermometer unavailable");
  1022.     else
  1023.     {
  1024.         in.Dialog = 1;
  1025.         memActivity++;
  1026.  
  1027.         itemizeThermo(initItem);
  1028.  
  1029.         ShowWindow(WNDTHM);
  1030.     }
  1031.  
  1032.     timeCursor = TickCount() + CURS_LATENT;
  1033.     saveRefNum = gameRefNum = levRefNum = -1;
  1034.     in.Recover = 1;
  1035. }
  1036.  
  1037. static void
  1038. continueRecover()
  1039. {
  1040.     restore_savefile();
  1041.  
  1042.     /* update the thermometer */
  1043.     if (in.Dialog && ! (in.Recover % 4))
  1044.         itemizeThermo(invalItem);
  1045.  
  1046.     if (in.Recover <= MAX_RECOVER_COUNT)
  1047.         return;
  1048.  
  1049.     endRecover();
  1050.  
  1051.     if (saveRezStrings())
  1052.         return;
  1053.  
  1054.     note(noErr, alidNote, "\pOK: Recovery succeeded");
  1055. }
  1056.  
  1057. /* no messages from here (since we might be quitting) */
  1058. static void
  1059. endRecover()
  1060. {
  1061.     in.Recover = 0;
  1062.  
  1063.     oldCursor = curs_Init;
  1064.     SetCursor(&qd.arrow);
  1065.  
  1066.     /* clean up abandoned files */
  1067.     if (gameRefNum >= 0)
  1068.         (void) FSClose(gameRefNum);
  1069.  
  1070.     if (levRefNum >= 0)
  1071.         (void) FSClose(levRefNum);
  1072.  
  1073.     if (saveRefNum >= 0)
  1074.     {
  1075.         (void) FSClose(saveRefNum);
  1076.         (void) FlushVol((StringPtr) 0L, vRefNum);
  1077.         /* its corrupted so trash it ... */
  1078.         (void) HDelete(vRefNum, dirID, savename);
  1079.     }
  1080.  
  1081.     saveRefNum = gameRefNum = levRefNum = -1;
  1082.  
  1083.     /* close the progress thermometer dialog */
  1084.     in.Dialog = 0;
  1085.     CloseDialog(DLGTHM);
  1086.     DisposHandle(dlgThermo.items);
  1087.     memActivity++;
  1088. }
  1089.  
  1090. /* add friendly, non-essential resource strings to save file */
  1091. static short
  1092. saveRezStrings()
  1093. {
  1094.     short            sRefNum;
  1095.     StringHandle    strHnd;
  1096.     short            i, rezID;
  1097.     unsigned char    *plName;
  1098.  
  1099.     HCreateResFile(vRefNum, dirID, savename);
  1100.  
  1101.     sRefNum = HOpenResFile(vRefNum, dirID, savename, fsRdWrPerm);
  1102.     if (sRefNum <= 0)
  1103.     {
  1104.         note(noErr, alidNote, "\pOK: Minor resource map error");
  1105.         return 1;
  1106.     }
  1107.  
  1108.     /* savename and hpid get mutilated here... */
  1109.     plName = savename + 5;                /* save/ */
  1110.     *savename -= 5;
  1111.     do
  1112.     {
  1113.         plName++;
  1114.         (*savename)--;
  1115.         hpid /= 10L;
  1116.     }
  1117.     while (hpid);
  1118.     *plName = *savename;
  1119.  
  1120.     for (i = 1; i <= 2; i++)
  1121.     {
  1122.         switch (i)
  1123.         {
  1124.         case 1:
  1125.             rezID = PLAYER_NAME_RES_ID;
  1126.             strHnd = NewString(* (Str255 *) plName);
  1127.             break;
  1128.  
  1129.         case 2:
  1130.             rezID = APP_NAME_RES_ID;
  1131.             strHnd = NewString(* (Str255 *) "\pNetHack");
  1132.             break;
  1133.         }
  1134.  
  1135.         if (! strHnd)
  1136.         {
  1137.             note(noErr, alidNote, "\pOK: Minor \'STR \' resource error");
  1138.             CloseResFile(sRefNum);
  1139.             return 1;
  1140.         }
  1141.  
  1142.         /* should check for errors... */
  1143.         AddResource((Handle) strHnd, 'STR ', rezID, * (Str255 *) "\p");
  1144.     }
  1145.  
  1146.     memActivity++;
  1147.  
  1148.     /* should check for errors... */
  1149.     CloseResFile(sRefNum);
  1150.     return 0;
  1151. }
  1152.  
  1153. static void
  1154. set_levelfile_name(long lev)
  1155. {
  1156.     unsigned char    *tf;
  1157.  
  1158.     /* find the dot.  this is guaranteed to happen. */
  1159.     for (tf = (lock + *lock); *tf != '.'; tf--, lock[0]--)
  1160.         ;
  1161.  
  1162.     /* append the level number string (pascal) */
  1163.     if (tf > lock)
  1164.     {
  1165.         NumToString(lev, * (Str255 *) tf);
  1166.         lock[0] += *tf;
  1167.         *tf = '.';
  1168.     }
  1169.     else    /* huh??? */
  1170.     {
  1171.         endRecover();
  1172.         note(noErr, alidNote, "\pSorry: File Name Error");
  1173.     }
  1174. }
  1175.  
  1176. static short
  1177. open_levelfile(long lev)
  1178. {
  1179.     OSErr    openErr;
  1180.     short    fRefNum;
  1181.  
  1182.     set_levelfile_name(lev);
  1183.     if (! in.Recover)
  1184.         return (-1);
  1185.  
  1186.     if ((openErr = HOpen(vRefNum, dirID, lock, fsRdWrPerm, &fRefNum))
  1187.             && (openErr != fnfErr))
  1188.     {
  1189.         endRecover();
  1190.         note(noErr, alidNote, "\pSorry: File Open Error");
  1191.         return (-1);
  1192.     }
  1193.  
  1194.     return (openErr ? -1 : fRefNum);
  1195. }
  1196.  
  1197. static short
  1198. create_savefile(unsigned char *savename)
  1199. {
  1200.     short    fRefNum;
  1201.  
  1202.     /* translate savename to a pascal string (in place) */
  1203.     {
  1204.         unsigned char    *pC;
  1205.         short            nameLen;
  1206.  
  1207.         for (pC = savename; *pC; pC++);
  1208.  
  1209.         nameLen = pC - savename;
  1210.  
  1211.         for ( ; pC > savename; pC--)
  1212.             *pC = *(pC - 1);
  1213.  
  1214.         *savename = nameLen;
  1215.     }
  1216.  
  1217.     if (HCreate(vRefNum, dirID, savename, CREATOR, SAVETYPE)
  1218.         || HOpen(vRefNum, dirID, savename, fsRdWrPerm, &fRefNum))
  1219.     {
  1220.         endRecover();
  1221.         note(noErr, alidNote, "\pSorry: File Create Error");
  1222.         return (-1);
  1223.     }
  1224.  
  1225.     return fRefNum;
  1226. }
  1227.  
  1228. static void
  1229. copy_bytes(short inRefNum, short outRefNum)
  1230. {
  1231.     char    *buf = (char *) pIOBuf;
  1232.     long    bufSiz = pBytes->memIOBuf;
  1233.  
  1234.     long    nfrom, nto;
  1235.  
  1236.     do
  1237.     {
  1238.         nfrom = read_levelfile(inRefNum, buf, bufSiz);
  1239.         if (! in.Recover)
  1240.             return;
  1241.  
  1242.         nto = write_savefile(outRefNum, buf, nfrom);
  1243.         if (! in.Recover)
  1244.             return;
  1245.  
  1246.         if (nto != nfrom)
  1247.         {
  1248.             endRecover();
  1249.             note(noErr, alidNote, "\pSorry: File Copy Error");
  1250.             return;
  1251.         }
  1252.     }
  1253.     while (nfrom == bufSiz);
  1254. }
  1255.  
  1256. static void
  1257. restore_savefile()
  1258. {
  1259.     static long    savelev;
  1260.     long        saveTemp, lev;
  1261.     xchar        levc;
  1262.  
  1263.     /* level 0 file contains:
  1264.      *    pid of creating process (ignored here)
  1265.      *    level number for current level of save file
  1266.      *    name of save file nethack would have created
  1267.      *    and game state
  1268.      */
  1269.  
  1270.     lev = in.Recover - 1;
  1271.     if (lev == 0L)
  1272.     {
  1273.         gameRefNum = open_levelfile(0L);
  1274.  
  1275.         if (in.Recover)
  1276.             (void) read_levelfile(gameRefNum, (Ptr) &hpid, sizeof(hpid));
  1277.  
  1278.         if (in.Recover)
  1279.             saveTemp = read_levelfile(gameRefNum, (Ptr) &savelev, sizeof(savelev));
  1280.  
  1281.         if (in.Recover && (saveTemp != sizeof(savelev)))
  1282.         {
  1283.             endRecover();
  1284.             note(noErr, alidNote, "\pSorry: \"checkpoint\" was not enabled");
  1285.             return;
  1286.         }
  1287.  
  1288.         if (in.Recover)
  1289.             (void) read_levelfile(gameRefNum, (Ptr) savename, sizeof(savename));
  1290.  
  1291.         /* save file should contain:
  1292.          *    current level (including pets)
  1293.          *    (non-level-based) game state
  1294.          *    other levels
  1295.          */
  1296.         if (in.Recover)
  1297.             saveRefNum = create_savefile(savename);
  1298.  
  1299.         if (in.Recover)
  1300.             levRefNum = open_levelfile(savelev);
  1301.  
  1302.         if (in.Recover)
  1303.             copy_bytes(levRefNum, saveRefNum);
  1304.  
  1305.         if (in.Recover)
  1306.             close_file(&levRefNum);
  1307.  
  1308.         if (in.Recover)
  1309.             unlink_file(lock);
  1310.  
  1311.         if (in.Recover)
  1312.             copy_bytes(gameRefNum, saveRefNum);
  1313.  
  1314.         if (in.Recover)
  1315.             close_file(&gameRefNum);
  1316.  
  1317.         if (in.Recover)
  1318.             set_levelfile_name(0L);
  1319.  
  1320.         if (in.Recover)
  1321.             unlink_file(lock);
  1322.     }
  1323.     else if (lev != savelev)
  1324.     {
  1325.         levRefNum = open_levelfile(lev);
  1326.         if (levRefNum >= 0)
  1327.         {
  1328.             /* any or all of these may not exist */
  1329.             levc = (xchar) lev;
  1330.  
  1331.             (void) write_savefile(saveRefNum, (Ptr) &levc, sizeof(levc));
  1332.  
  1333.             if (in.Recover)
  1334.                 copy_bytes(levRefNum, saveRefNum);
  1335.  
  1336.             if (in.Recover)
  1337.                 close_file(&levRefNum);
  1338.  
  1339.             if (in.Recover)
  1340.                 unlink_file(lock);
  1341.         }
  1342.     }
  1343.  
  1344.     if (in.Recover == MAX_RECOVER_COUNT)
  1345.         close_file(&saveRefNum);
  1346.  
  1347.     if (in.Recover)
  1348.         in.Recover++;
  1349. }
  1350.  
  1351. static long
  1352. read_levelfile(short rdRefNum, Ptr bufPtr, long count)
  1353. {
  1354.     OSErr    rdErr;
  1355.     long    rdCount = count;
  1356.  
  1357.     if ((rdErr = FSRead(rdRefNum, &rdCount, bufPtr)) && (rdErr != eofErr))
  1358.     {
  1359.         endRecover();
  1360.         note(noErr, alidNote, "\pSorry: File Read Error");
  1361.         return (-1L);
  1362.     }
  1363.  
  1364.     return rdCount;
  1365. }
  1366.  
  1367. static long
  1368. write_savefile(short wrRefNum, Ptr bufPtr, long count)
  1369. {
  1370.     long    wrCount = count;
  1371.  
  1372.     if (FSWrite(wrRefNum, &wrCount, bufPtr))
  1373.     {
  1374.         endRecover();
  1375.         note(noErr, alidNote, "\pSorry: File Write Error");
  1376.         return (-1L);
  1377.     }
  1378.  
  1379.     return wrCount;
  1380. }
  1381.  
  1382. static void
  1383. close_file(short *pFRefNum)
  1384. {
  1385.     if (FSClose(*pFRefNum) || FlushVol((StringPtr) 0L, vRefNum))
  1386.     {
  1387.         endRecover();
  1388.         note(noErr, alidNote, "\pSorry: File Close Error");
  1389.         return;
  1390.     }
  1391.  
  1392.     *pFRefNum = -1;
  1393. }
  1394.  
  1395. static void
  1396. unlink_file(unsigned char *filename)
  1397. {
  1398.     if (HDelete(vRefNum, dirID, filename))
  1399.     {
  1400.         endRecover();
  1401.         note(noErr, alidNote, "\pSorry: File Delete Error");
  1402.         return;
  1403.     }
  1404. }
  1405. #endif
  1406.